/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.portmgr.core.oper.snmp;

import hxg.portmgr.core.device.dslam.LineProfile;
import hxg.portmgr.core.device.intf.Intf;
import hxg.portmgr.core.device.intf.IntfState;
import hxg.portmgr.core.oper.snmp.oidgroup.*;
import hxg.portmgr.core.oper.snmp.param.AdslParam;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UnsignedInteger32;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;

/**
 *
 * @author hxg
 */
public class DslamSnmpOper extends SnmpOper {

    private static final Logger logger = Logger.getLogger(DslamSnmpOper.class.getName());
    private String adslLineConfProfile = "1.3.6.1.2.1.10.94.1.1.1.1.4";
    private static final OID adslLineConfProfileRowStatusOid = new OID(".1.3.6.1.2.1.10.94.1.1.14.1.30");

    public DslamSnmpOper(String ip, String readCommunity, String writeCommunity) {
        super(ip, readCommunity, writeCommunity);
    }

    /**
     * 如果OID不同的话，请覆盖getAdslPorfileConfOidEntries()和getAdslProfileNameOidEntry()方法
     * 注意，该方法返回的是
     *
     * @return
     * @throws IOException
     */
    public List<LineProfile> getLineProfiles() throws IOException {
        List<LineProfile> profs = new ArrayList<>();
        AdslOidEntry[] adslProfileConfOidEnties = getAdslPorfileConfOidEntries();


        OID[] oids = new OID[adslProfileConfOidEnties.length];
        for (int i = 0; i < oids.length; i++) {
            oids[i] = adslProfileConfOidEnties[i].getOID();
        }
        Map<OID, Map<OID, Variable>> mp = SnmpOperUtils.getVariablelTableOfOIDs(oids, new int[]{6, 6}, SnmpOperUtils.getTarget(ip, readCommunity));
        for (OID ifidx : mp.keySet()) {
            LineProfile prof = new LineProfile();
            prof.setIndex(ifidx.toString());
            Map<OID, Variable> oidmp = mp.get(ifidx);
            EnumMap<AdslParam, Integer> valueMap = new EnumMap<>(AdslParam.class);
            for (AdslOidEntry oidentry : adslProfileConfOidEnties) {
                switch (oidentry.getParamType()) {
                    case LineProfileName:
                        //<editor-fold defaultstate="collapsed" desc="设置LineProfile的名字">
                        if (oidmp.containsKey(oidentry.getOID())) {
                            String name = oidmp.get(oidentry.getOID()).toString();
                            prof.setName(name);
                        } else {
                            byte[] ints = ifidx.toByteArray();
                            prof.setName(new String(ints));
                        }
                        break;
                    //</editor-fold>
                    case LineProfileRateMode:
                        if (oidmp.containsKey(oidentry.getOID())) {
                            int mode = oidmp.get(oidentry.getOID()).toInt();
                            prof.setRateMode(mode);
                        } else {
                            prof.setRateMode(0);
                        }
                        break;
                    default:
                        if (oidmp.containsKey(oidentry.getOID())) {
                            int val = oidmp.get(oidentry.getOID()).toInt();
                            valueMap.put(oidentry.getParamType(), val);
                        }
                        break;
                }

            }
            prof.addProfileValueMap(valueMap);
            profs.add(prof);
        }
        return profs;
    }

    /**
     * 规范： 由于不是所有设备都支持，所以不支持该方法的设备需要覆盖本方法，并返回Null值
     *
     * @param profName 模板名称
     * @return 新的模板
     * @throws IOException \
     */
    public LineProfile createProfile(String profName, String ifidx, Map<AdslParam, Integer> mp) throws IOException {
        LineProfile prof = new LineProfile();
        prof.setName(profName);
        prof.setIndex(ifidx);
        prof.addProfileValueMap(mp);
        createProfile(prof);
        return prof;
    }

    public LineProfile createProfile(LineProfile profile) throws IOException {
        String index = profile.getIndex();
        OID indexOid;
        //注意，新建的PROFILE有时Index希望自己定制，需要将
        if (index != null && !index.isEmpty()) {
            indexOid = new OID(index);
        } else {
            indexOid = new OctetString(profile.getName()).toSubIndex(false);
        }
        List<VariableBinding> vblist = new ArrayList<>();
        vblist.add(new VariableBinding(AdslLineProfileConfOID.adslAtucConfRateChanRatio.getOID(), new Integer32(profile.getRateModeIntValue())));
        vblist.add(new VariableBinding(AdslLineProfileConfOID.adslAturConfRateChanRatio.getOID(), new Integer32(profile.getRateModeIntValue())));
        vblist.add(new VariableBinding(AdslLineProfileConfOID.adslAturChanConfFastMaxTxRate.getOID(), new UnsignedInteger32(profile.getValue(AdslParam.FastUp))));
        vblist.add(new VariableBinding(AdslLineProfileConfOID.adslAturChanConfInterleaveMaxTxRate.getOID(), new UnsignedInteger32(profile.getValue(AdslParam.InterLeavedUp))));
        vblist.add(new VariableBinding(AdslLineProfileConfOID.adslAtucChanConfFastMaxTxRate.getOID(), new UnsignedInteger32(profile.getValue(AdslParam.FastDown))));
        vblist.add(new VariableBinding(AdslLineProfileConfOID.adslAtucChanConfInterleaveMaxTxRate.getOID(), new UnsignedInteger32(profile.getValue(AdslParam.InterLeavedDown))));
        VariableBinding[] vbs = vblist.toArray(new VariableBinding[0]);

        /**
         * 这里有个问题：有些设备第一次加的时候的值死活不能设置参数，所以后不一个set
         */
        ResponseEvent createRow = SnmpOperUtils.createRow(adslLineConfProfileRowStatusOid, indexOid, vbs, SnmpOperUtils.getTarget(ip, readCommunity));
        SnmpOperUtils.set(vbs, SnmpOperUtils.getTarget(ip, writeCommunity));
        return profile;
    }

    public LineProfile createProfile(String profName) throws IOException {
        LineProfile prof = new LineProfile();
        OID indexOid = new OctetString(profName).toSubIndex(false);
        VariableBinding vb = new VariableBinding(new OID(".1.3.6.1.2.1.10.94.1.1.14.1.2"), new Integer32(0));
        SnmpOperUtils.createRow(adslLineConfProfileRowStatusOid, indexOid, new VariableBinding[]{vb}, SnmpOperUtils.getTarget(ip, readCommunity));
        return prof;
    }

    public void removeProfile(LineProfile prof) throws IOException {
        String index = prof.getIndex();
        OID indexOid;
        //注意，新建的PROFILE有时Index希望自己定制，需要将String值转换为Index值
        if (index != null && !index.isEmpty()) {
            indexOid = new OID(index);
        } else {
            indexOid = new OctetString(prof.getName()).toSubIndex(false);
        }
        SnmpOperUtils.destoryRow(adslLineConfProfileRowStatusOid, indexOid, SnmpOperUtils.getTarget(ip, readCommunity));
    }

    public LineProfile removeProfile(OID indexOid) throws IOException {
        SnmpOperUtils.destoryRow(adslLineConfProfileRowStatusOid, indexOid, SnmpOperUtils.getTarget(ip, readCommunity));
        return new LineProfile();
    }

    /**
     * public List<Intf> getAdslIntfBySnmp(final Intf[] intfs) { ArrayList<Intf>
     * infs = new ArrayList<Intf>(); for (Intf inf : intfs) { if (inf.getType()
     * == 94) { infs.add(inf); } } return infs; }
     */
    final public void ModifyPorfile(Intf inf, String profileName) throws IOException {
        ModifyPorfile(inf.getIndex(), profileName);
    }

    public void ModifyPorfile(String ifidx, String profileName) throws IOException {
        //未测试的方法
        //先关闭端口，然后修改模板，在打开端口
        ShutdownPort(ifidx);
        String oid = adslLineConfProfile + "." + ifidx;
        SnmpOperUtils.set(oid, profileName, SnmpOperUtils.getTarget(ip, writeCommunity));
        Logger.getLogger(DslamSnmpOper.class.getName()).log(Level.INFO, "发送{0}到IP[{1}],值{2}", new Object[]{oid, ip, profileName});
        TurnOnPort(ifidx);        
    }
    
    //测试通过
    public String getLineProfile(String ifidx) throws IOException {
        String profileName = SnmpOperUtils.get(getIntfLineProfileOid().append(ifidx), SnmpOperUtils.getTarget(ip, readCommunity)).toString();
        return profileName;
    }

    public AdslOidEntry[] getLineStateOidEntries() {
        AdslOidEntry[] oidgroup = new AdslLineOID[]{
            AdslLineOID.adslAtucCurrAtn,
            AdslLineOID.adslAtucCurrAttainableRate,
            AdslLineOID.adslAtucCurrOutputPwr,
            AdslLineOID.adslAtucCurrSnrMgn,
            AdslLineOID.adslAturCurrAtn,
            AdslLineOID.adslAturCurrAttainableRate,
            AdslLineOID.adslAturCurrOutputPwr,
            AdslLineOID.adslAturCurrSnrMgn
        };
        return oidgroup;
    }

    final public AdslParam[] getLineStateParam() {
        Set<AdslParam> set = new LinkedHashSet<>();
        for (AdslOidEntry entry : getLineStateOidEntries()) {
            set.add(entry.getParamType());
        }
        AdslParam[] params = set.toArray(new AdslParam[0]);
        return params;
    }

    public AdslOidEntry[] getAdslPorfileConfOidEntries() {
        AdslOidEntry[] oidgroup = new AdslOidEntry[]{
            AdslLineProfileConfOID.adslLineConfProfileName,
            AdslLineProfileConfOID.adslAtucConfRateChanRatio,
            AdslLineProfileConfOID.adslAtucChanConfFastMaxTxRate,
            AdslLineProfileConfOID.adslAtucChanConfInterleaveMaxTxRate,
            AdslLineProfileConfOID.adslAturChanConfFastMaxTxRate,
            AdslLineProfileConfOID.adslAturChanConfInterleaveMaxTxRate
        };
        return oidgroup;
    }

    /**
     * 批量获取接口配置的线路模板
     *
     * @param intfs 要求ADSL
     * @return
     */
    public LinkedHashMap<Intf, String> getLineProfile(Intf[] intfs) throws IOException {
        LinkedHashMap<Intf, String> mp = new LinkedHashMap<>();
        //String[] profs = new String[intfs.length];
        OID[] oids = new OID[intfs.length];
        for (int i = 0; i < oids.length; i++) {
            oids[i] = new OID(getIntfLineProfileOid().append(intfs[i].getIndex()));
            logger.log(Level.FINE, "生成了OID：{0}", new Object[]{oids[i]});
            //System.out.println("生成了OID：" + oids[i]);
        }
        Variable[] vars = SnmpOperUtils.get(oids, 8, SnmpOperUtils.getTarget(ip, readCommunity));
        //System.out.println("VARS Length:" + vars.length);
        for (int i = 0; i < oids.length; i++) {
            String prof;
            if (vars[i] != null) {
                if (!vars[i].isException()) {
                    prof = vars[i].toString();
                } else {
                    prof = vars[i].getSyntaxString();
                }
            } else {
                logger.log(Level.WARNING, "接口{0},index={1},OID={2},对应的数据为,异常;设备为{3},IP={4}", new Object[]{
                    intfs[i].getMarkName(), intfs[i].getIndex(), oids[i], intfs[i].getDevice().getChsName(), ip
                });
                prof = "NULL";
            }
            mp.put(intfs[i], prof);
        }
        return mp;
    }

    protected OID getIntfLineProfileOid() {
        return new OID(adslLineConfProfile);
    }

    public EnumMap<AdslParam, Value>[] getLineState(Intf[] intfs) throws IOException {
        String[] intfidxs = new String[intfs.length];
        for (int i = 0; i < intfs.length; i++) {
            intfidxs[i] = intfs[i].getIndex();
        }
        AdslOidEntry[] oids = getLineStateOidEntries();
        Map<AdslOidEntry, Variable>[] mpsEntry = SnmpOperUtils.getIntfVariblesOfIntf(intfidxs, oids, SnmpOperUtils.getTarget(ip, readCommunity));
        EnumMap<AdslParam, Value>[] mps = new EnumMap[intfs.length];
        for (int i = 0; i < intfs.length; i++) {
            EnumMap<AdslParam, Value> mp = new EnumMap<>(AdslParam.class);
            for (Entry<AdslOidEntry, Variable> entry : mpsEntry[i].entrySet()) {
                AdslParam param = entry.getKey().getParamType();
                Value val = new Value(entry.getValue(), param.remoteValueType());
                mp.put(param, val);
            }
            mps[i] = mp;
        }
        return mps;
    }

    /**
     * 批量获取接口线路质量
     *
     * @return Map<接口索引, Map<AdslParam, Value>> 其中接口索引不是以.开头
     */
    @Deprecated
    final public Map<String, Map<AdslParam, Value>> getLineStateMap(Intf[] intfs) throws IOException {
        AdslOidEntry[] oids = getLineStateOidEntries();
        Map<OID, Map<AdslOidEntry, Variable>> varmp = SnmpOperUtils.getVariblesOfOidEntryByRow(oids, SnmpOperUtils.getTarget(ip, readCommunity));
        Map<String, Map<AdslParam, Value>> retmp = new LinkedHashMap<>();
        for (Entry<OID, Map<AdslOidEntry, Variable>> entry : varmp.entrySet()) {
            String ifidx = entry.getKey().toString();
            EnumMap<AdslParam, Value> emp = new EnumMap<>(AdslParam.class);
            for (Entry<AdslOidEntry, Variable> paramEntry : entry.getValue().entrySet()) {
                AdslParam param = paramEntry.getKey().getParamType();
                Value val = new Value(paramEntry.getValue(), param.remoteValueType());
                emp.put(param, val);
            }
            retmp.put(ifidx, emp);
        }
        return retmp;
    }

//    final protected Map<AdslOidEntry, Variable> getLineStateMap(String ifidx) throws IOException {
//        return getLineStateMap(ifidx, getLineStateOidEntries());
//        //return SnmpOperUtils.getIntfTableVaribles(ifidx, getLineStateOidEntries(), SnmpOperUtils.getTarget(ip, readCommunity));
//    }
    final protected Map<AdslOidEntry, Variable> getLineState(String ifidx, AdslOidEntry[] oidentries) throws IOException {
        return SnmpOperUtils.getIntfVariblesOfIntf(ifidx, oidentries, SnmpOperUtils.getTarget(ip, readCommunity));
    }

    public EnumMap<AdslParam, Value> getLineState(String ifidx, AdslParam[] params) throws IOException {
        AdslOidEntry[] oidentries = new AdslOidEntry[params.length];
        for (int i = 0; i < params.length; i++) {
            for (AdslOidEntry oidEntry : getLineStateOidEntries()) {
                if (oidEntry.getParamType() == params[i]) {
                    oidentries[i] = oidEntry;
                }
            }
        }
        Map<AdslOidEntry, Variable> mp = getLineState(ifidx, oidentries);
        EnumMap<AdslParam, Value> ret_mp = new EnumMap<>(AdslParam.class);
        for (AdslOidEntry oidEntry : mp.keySet()) {
            AdslParam param = oidEntry.getParamType();
            ret_mp.put(param, new Value(mp.get(oidEntry), param.remoteValueType()));
        }
        return ret_mp;
    }

    /**
     * 获取线路质量的方法，首选 如果需要获得Map<Adslparam, Variable> 那么，需要自己进行转换
     *
     * @param ifidx
     * @return
     * @throws IOException
     */
    public EnumMap<AdslParam, Value> getLineState(String ifidx) throws IOException {
        return getLineState(ifidx, getLineStateParam());
    }

    @Override
    /**
     * 华为的DSLAM可能会用65536表示在端口acting状态,也可能是5(这个到时标准值,表示等待连接的状态)
     */
    protected IntfState getOperState(String ifidx) throws IOException {
        //SnmpEntity se = new SnmpEntity(ip, 161, readCommunity);
        Variable var = SnmpOperUtils.get(IfOID.ifOperState.oidValue() + "." + ifidx, SnmpOperUtils.getTarget(ip, readCommunity));
        int val = -1;
        if (var != null) {
            val = var.toInt();
        }
        switch (val) {
            case 1:
                return IntfState.OperUp;
            case 2:
                return IntfState.OperDown;
            case 5:
                return IntfState.OperDown;
            case 65536:
                return IntfState.OperDown;
            default:
                return IntfState.OperDown;
        }
        //return val;
    }
}
